;	29-Jul-85 fixed code at CLCSUB to prevent int 0, same ver 7.5-E
;	21-Jul-85 revised for 32 bit mult/div in GTLSEC and CLCSUB routines
;		changed to rev 7.5-E. WHAT A MESS....
;	29-Mar-85 relaxed for CCP/M ver 4.1 and up changed to -D ver
;	8-11-84 revsed for PC_MODE support renamed DU-V75C.A86
;	04/15/84 revised to correct minor bugs - renamed DU-V75B.A86
;
;	  DU.ASM  V7.5	Revised 1/23/81 ( CP/M-80 version )
;	DISK UTILITY - By Ward Christensen
;
;	DU-75A.A86 is a CP/M-86 translation of the CP/M-80 version.
;	
;	The original translation came from the SLICER USER GROUP
;	disk #2, via Micro Cornucopia, PO Box 223, Bend, OR
;	It did not indicate the name of the translator who should
;	be thanked for his work.  That version appeared as DU-75.A86
;
;	See DU77.DOC for description and detailed instructions.
;	See original issue ver 7.5 for history of changes and authors
;	NOTE: see below for change in multiple cmd symbol.
;
; 	03/05/84 by H.M. Van Tassell, (201)-755-5372
;		 120 Hill Hollow  Road, Watchung, NJ 07060 
;
;	Added direct BIOS disk routines for CP/M-86 Plus ver 3.1
;	and CCP/M-86 ver 3.1, with minimum changes to get it to work.
;	There is now a check for version number with exit if unknown.
;
;	Fixed a bug with Map when using directory initialized for time
;	stamping, added user&drive to prompt, and changed the multiple
;	command symbol from ';' to '!'. *** THIS IS CP/M CONVENTION ***
;
;	Changed LISTOUT to use BDOS rather than BIOS call since under
;	CCP/M-86, a BIOS call to LIST must have it's device number in
;	register DL. The BDOS call uses the default list device and DU
;	will "own" the printer once it is called using L_WRITE.
;
;	to generate: ASM86 DU ! GENCMD DU 8080 CODE[MF00]
;							
;		----------------
;

L_WRITE		equ	5	;write to default list device
S_BIOS		equ	50	;direct bios call
DRV_DPB		equ	31	;get drive DPB, returns SYSDAT in ES


; bios disk call data structure
;
SELECT_DISK	equ  word ptr 0[bx]
SET_TRACK	equ  word ptr 2[bx]
SET_DMASEG	equ  word ptr 4[bx]
SET_DMAOFF	equ  word ptr 6[bx]
SET_SECTOR	equ  word ptr 8[bx]
READ_SECTOR	equ  word ptr 10[bx]
WRITE_SECTOR	equ  word ptr 12[bx]
SECTOR_XLAT	equ  word ptr 14[bx]
HOME_DISK	equ  word ptr 16[bx]


BIO_SELDSK 	equ	9		;BIOS function number
BIO_READ  	equ	10		;BIOS function number
BIO_WRITE  	equ	11		;BIOS function number

; partial data structure of Disk Parameter Header (DPH)
;
DPH_XLATE	equ	word ptr 0	;offset to xlate table
LOG_SEQN	equ	byte ptr 6	;to force reset of permanent media
DPB_PTR31	equ	word ptr 8	;offset of DPB pointer in DPH (v3.1)
DPB_PTR11	equ	word ptr 10	;offset in version 1.1
DPB_SIZE	equ	17		;size of Disk Parameter Block


;------- for CP/M ver 3.1 & CCP/M ver 3.1 ----
BIOS_ENTRY  	equ	dword ptr .28h	;loc of BIOS entry in SYStem DATa

;------------ for CP/M ver 3.1 --------------
UDA_SEG	  	equ	word ptr .4eh	;loc of UDA seg in SYStem DATa area

;------------- for CCP/M ver 3.1 -------------
S_SYSDAT	equ	154		;get SYStem DATa area addr
P_PDADR		equ	156		;get PDA address

CCPM_31		equ	1431h		;CCP/M-86 version number

P_UDA		equ	word ptr 10h	;loc of UDA seg in User Data Area
;-------------------------------------------------------------------------
;
;System equates
;
BASE	EQU	0
;
	ORG BASE +5CH
FCB	DB 0
;
PRINT	EQU	9
RESETDK	EQU	13
SELDK	EQU	14
SRCHF	EQU	17	;SEARCH FIRST
SUSER	EQU	32
GETDSK	EQU	25
GETDPB	EQU	31
;
;	BIOS
;
CONSTF	EQU	2
CONINF	EQU	3
CONOUTF	EQU	4
LISTF	EQU	5
;
S2OFF	EQU	14	;OFFSET INTO FCB FOR S2 BYTE
S2MASK	EQU	0FH	;MASK FOR EXTENDED RC BITS OF S2
DPBLEN	EQU	15	;SIZE OF CP/M 2.x DISK PARM BLOCK
;
;
;Define ASCII characters
;
CR	EQU	0DH	;CARRIAGE RETURN
LF	EQU	0AH	;LINE FEED
TAB	EQU	09H	;TAB
BS	EQU	08H	;BACKSPACE
;
	CSEG
	ORG 100H
;
	JMP PASTCK	;JUMP OVER CLOCK BYTE AND I.D.
;
BCONST:
	MOV AL,CONSTF
	JMP BIOS
;
BCONIN:
	MOV AL,CONINF
	JMP BIOS
;
BCONOUT:
	MOV AL,CONOUTF
	JMP BIOS
;
BLIST:
	MOV AL,LISTF
	JMP BIOS
;------------------------------------------------------------------------
BHOME:
	mov bx,bios_call_tbl
	jmp home_disk
;
SEL:
	mov bx,bios_call_tbl
	jmp select_disk
;
TRK:
	mov bx,bios_call_tbl
	jmp set_track
;
SEC:
	mov bx,bios_call_tbl
	jmp set_sector
;
DMA:
	mov bx,bios_call_tbl
	jmp set_dmaoff
;
DSKREAD:
	mov bx,bios_call_tbl
	jmp read_sector
;
DSKWRITE:
	mov bx,bios_call_tbl
	jmp write_sector
;
BSECTTRAN:
	mov bx,bios_call_tbl
	jmp sector_xlat
;
SETDMAB:
	mov bx,bios_call_tbl
	jmp set_dmaseg
;


; checks cpm version and initializes things
cpmchk:
	mov	cl,12			;get version number
	int	224
	mov	cpm_version,ax		;and save it
	test	ah,01h			;is it mp/m ?
	jnz 	is_mpm
	cmp	al,22h			;is it version 11?
	je	ver_11
	cmp	al,31h			;is it version 31?
	jae	ver_31
					;else it is unknown version
unk_ver:				;say unknown and exit
	mov	dx,offset wvmsg
	mov	cl,9
	int	224
	jmp	exit
is_mpm:					;MP/M is not supported
	mov	dx,offset mpmsg		;say it and exit
	mov	cl,9
	int	224
	jmp	exit

ver_11:
	mov	bios_call_tbl,offset table11
	jmps	ver_ok
ver_31:
	mov	bios_call_tbl,offset table31
ver_ok:
	mov ax,ds			;setup our dseg for
	mov idmaseg,ax			;sector buffer read/write
	mov dmaseg,ax			
	mov dmaoff,80h			;and default buffer
	ret


; direct bios call for cp/m-86 version 1.1
;
select_disk11:
	mov	al,9			;bios fucn 9
	jmps	bios			;returns dph addr in es:bx
set_track11:
	mov	al,10
	jmps	bios
set_dmaseg11:
	mov	al,17
	jmps	bios
set_dmaoff11:
	mov	al,12
	jmps	bios
set_sector11:
	mov	al,11
	jmps	bios
read_sector11:
	mov	al,13
	jmps	bios
write_sector11:
	mov	al,14
	jmps	bios
sector_xlat11:
	mov	al,16
	jmps	bios
home_disk11:
	mov	al,8 
	jmps	bios

;
bios:
	mov bpb_func,al		;bios function number
	mov bpb_cx,cx
	mov bpb_dx,dx
	mov dx,offset bpb
	mov cl,S_BIOS		;direct bios function call = 50
	
bdos:	int 224			;fall thru
	ret

;
; direct bios disk calls for CP/M-86 version 3.1
;
set_sector31:
	call logi_2_phy		;convert logical record number to
	mov isector,cx		;physical sector number
	ret

set_track31:
	mov itrack,cx
	ret

sector_xlat31:
	call pseudo_xlat	;translate logical records
	ret			;BX = pseudo xlate record number

set_dmaseg31:			
	mov dmaseg,cx		;user dma segment
	ret

set_dmaoff31:
	mov dmaoff,cx		;user dma offset
	ret

home_disk31:			;do nothing, there aint no home
	xor ax,ax		;but make sure al=0
	ret

pseudo_xlat:
;---------
;	ENTRY:	CX = logical record number (relative 1)
;	EXIT:	BX = pseudo logical sector number
;		     (gives same result as if disk had same number of
;		      physical sectors as there are 128 byte records)

	mov bx,cx		;just in case there is no xlate
	cmp sec_xlat_tbl,0 ! jz no_xlat ;is there an xlate table?
	inc cx			;do this only for DU-V75
	call logi_2_phy		;returns AX = physical sector
	mov bx,sec_xlat_tbl	;point to disk xlate table
	push ds ! mov ds,sysaddr ;in the SYStem DATa area
	xlat sec_xlat_tbl	;AL = translated phy sector number
	pop ds
	mov cl,PHYSHF		;get physical shift factor
	shl ax,cl			
	add ax,rec_offset	;add in the record offset into sector
	inc ax			;make it relative to 1
	mov bx,ax		;move to BX
no_xlat:
	ret
 
logi_2_phy:
;----------
;	ENTRY:	CX = logical record number (relative to 1)
;	EXIT:	CX=AX = physical sector number (relative to 0)
;		rec_offset contains record offset into phy sector
;
	dec cx			;now relative to zero
	mov ax,cx 		;move logical record number
	xor bh,bh		;clear high byte 
	mov bl,PHYMSK		;get physical mask
	and cx,bx		;CX = logical record offset into
	mov rec_offset,cx	;...physical sector
	mov cl,PHYSHF		;get physical shift factor
	shr ax,cl		
	mov cx,ax		;CX=AX = physical sector number
	ret


;++++++++++++++++++++++++++++++++++++++
select_disk31:		;selects a drive
;-------------
; resets login sequence number of drive to 0, to force
; permanent media to be logged in again on disk reset
;	Entry:	CL = drive to select
;		DL = 0 if initial select, else 1

	mov trk_sect,0ffffh		;indicate not in memory
	mov idrive,cl			;put drive in table
	push es ! push ds		;save context
	push cx				;save drive
	call getsu			;set up DS and ES
	pop cx				;restore drive
	mov ax,BIO_SELDSK		;do the BIOS SELDSK call
	callf BIOS_ENTRY		;call the BIOS thru entry point
	cmp bx,0 ! jz sel_error         ;bx = 0 is an illegal drive

	mov LOG_SEQN[bx],0		;to force a disk reset set the
					;...login sequence no. to zero
	mov cx,DPH_XLATE[bx]		;get xlate table offset in SYSDAT
					;copy DPB to local storage
	pop es ! push es		;get our dseg into ES
	mov es:sec_xlat_tbl,cx		;save xlate table address
	mov di,offset dpb		;setup dest of dpb
	mov si,DPB_PTR31[bx]		;get the info from DPH (ver 31)

	cmp Word Ptr[si],0FFFFh		; is this a PC_MODE dpb?
	jne not_pc_dpb
	  add si,12			;yes, correct for difference
not_pc_dpb:

	mov cx,DPB_SIZE
	rep movsb			;copy DPB into local storage
sel_error:
	pop ds ! pop es			;restore context
	mov cl,PHYSHF			; This is a dirty trick
	shl SPT,cl			; to get a SPT for DU
	
	cmp PHYSHF,4  			;make sure sector_buf is OK
	jbe buf_ok			; >>> PHYSHF TABLE <<<
	  mov dx,offset toobig		; 128 = 0	1024 = 3
	  mov cl,9			; 256 = 1	2048 = 4
	  int 224			; 512 = 2	4096 = 5
	  jmp exit
buf_ok:
	ret

sect_to_mem: 
;------------
;	ENTRY: 	itrack & isector specified
;	EXIT: 	sector is in memory, does read if required
;		AX BX = 0 if no error
;
	mov ax,itrack			;get track
	cmp ax,0 ! jnz not_zero
	  cmp ax,1 ! jmps no_match
not_zero:
	mov bx,isector			;and sector numbers
	cmp ax,trk_sect	! jne no_match	;if same as last time
	cmp bx,trk_sect+2		;then sector is in memory
no_match:
	mov trk_sect,ax			;save for next time
	mov trk_sect+2,bx
	mov ax,0 ! mov bx,ax		;clear AX BX registers
	je got_sect			;if not in memory, then
	mov bx,BIO_READ			;signal a read
	call biosiopb			;read a physical sector
got_sect:
	ret

read_sector31:	;reads a logical record from disk to buffer
;-------------
;
	call sect_to_mem		;get sector
	push si ! push di ! push es
	cld ! mov es,dmaseg		;setup user dma segment
	mov di,dmaoff			;where to copy 128 byte record
	mov si,rec_offset		;record offset into sector buffer
	mov cl,7 ! shl si,cl		;times 128
	add si,offset sector_buf	;points to start of record
	mov cx,128/2			;move 128 bytes
	rep movsw			;so do the move
	pop es ! pop di ! pop si
	ret
	

write_sector31:	;writes a physical sector
;--------------
;
	call sect_to_mem
	push si ! push di ! push es ! push ds
	mov ds,dmaseg			;source of user data to copy
	mov si,dmaoff			;into sector buffer
	cld ! mov es,idmaseg		;setup for sector buffer dma segment
	mov di,rec_offset		;record offset into sector buffer
	mov cl,7 ! shl di,cl		;times 128
	add di,offset sector_buf	;points to start of record
	mov cx,128/2			;move 128 bytes
	rep movsw			;so do the move
	pop ds ! pop es ! pop di ! pop si
	mov bx,BIO_WRITE		;signal a write
					;fall thru to write sector to disk
biosiopb:	;put the IOPB on the stack, call BIOS
	push ds				;ds will contain SYSDAT seg
	push es				;es will contain UDA seg
					;push iopb onto stack
	mov ah,imcnt
	mov al,idrive
	push ax				;drive and multi-sector count
	push itrack			;track #
	push isector			;sector # = 0
	push idmaseg			;sector buffer DMA segment
	push idmaoff			;sector buffer DMA offset

	call getsu			;set up DS-SYSDAT and ES-UDA
	mov ax,bx			;set I/O function into AX
	callf BIOS_ENTRY		;call indirect the BIOS
					;AL,BL = return status
	add sp,10			;restore stack
	pop es				;restore original ES
	pop ds				;ditto for DS
	ret

;======
getsu:	
;======
;	entry:	DS = local data seg
;	exit:	DS = SYSDAT seg, ES=UDA seg (for call to XIOS)

	mov ax,udaaddr			;get the saved value
	or ax,ax			;set flags
	jz get_ds_es			;uninitialized, go get DS and ES
	  mov es,ax			;we've been here before, so load regs
	  mov ds,sysaddr
	  ret

get_ds_es:				;this is the initial call
;---------

	cmp 	cpm_version,CCPM_31	;is it CCP/M-86 version 1431+?
	jb	get_ds_es10		;or version 1031

get_ds_es14:	;use this for CCP/M-86 version 1431
;----------
	mov cl,P_PDADR			;will return Process Desc Addr in BX
	int 224				;...and SYStem DATa seg in ES
	mov ax,es:P_UDA[bx]		;grab UDA_seg
	jmps com_su			;jmp to common
	
get_ds_es10:	;use this with CP/M-86 version 1031
;----------
	mov cl,DRV_DPB			;will return SYStem DATa seg in ES
	int 224
	mov ax,es:UDA_seg		;grab UDA_seg
					;fall thru to common 
com_su:		;this is common to both versions

	mov sysaddr,es			;save system data segment (SYSDAT)
	push es
	mov udaaddr,ax			;save for UDS_seg for future calls
	mov es,ax			;get UDA_seg into ES
	pop ds				;get SYSDAT into DS
	ret

;-----------------------------------------------------------------------
;
CLOCK	DB	1	;<---PUT NON-ZERO HERE FOR 4 MHZ CLOCK
;
PASTCK:
	MOV AX,CS
	MOV DS,AX
	MOV SS,AX
	MOV ES,AX
	MOV SP,OFFSET STACK
	
	call cpmchk		;make sure right version, do setup
;
HELLO:	CALL ILPRT
	DB	CR,LF,'DISK UTILITY ver 7.5-E',CR,LF
	DB	'For CP/M-86 & CCP/M-86 ver 3.1-4.1',CR,LF
	DB	CR,LF
	DB	'Type ? for help, X or ESC Quits'
	DB	CR,LF,0

	CALL GETSTP	;SET UP PARAMETERS
	MOV BX,OFFSET BASE +80H	;TO INPUT BUFF
	MOV AL,BYTE PTR [BX]
	OR AL,AL
	JZ PRMPTR	;NO COMMAND
;
;Got initial command, set it up
	MOV CH,AL	;SAVE LENGTH
	DEC CH
	JZ PRMPTR
	MOV DX,OFFSET INBUF
	INC BX	;SKIP LEN
	INC BX	;SKIP ' '
	CALL MOVE
	MOV AL,CR
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX
	MOV BX,OFFSET INBUF
	JMP PRMPTI
;
PRMPTR:	XOR AL,AL
	MOV  QFLAG,AL
	CALL RDBUF
;
PRMPTI:	MOV AL,255
	MOV  BYTE PTR TOGO,AL	;LOOP COUNT FOR "/"
	MOV  BYTE PTR TOGO+1,AL
;
PROMPT	EQU	$
SETSTK:
	MOV SP, OFFSET STACK
	XOR AL,AL	;ZERO 2-UP PRINT
	MOV  TWOUP,AL	;..SWITCH
	MOV AL,1
	MOV  FTSW,AL	;TELL SEARCH NOT TO INCR
	PUSH BX
	MOV BX,OFFSET BASE +100H
	MOV BUFAD,BX	;FOR RDBYTE
	POP BX
	CALL CTLCS	;ABORT?
	JZ PRMPTR	;..YES, READ BUFFER
;
;Do we have to position in directory after find?
	MOV AL,FINDFL
	OR AL,AL
	JZ L@00005
	JMP POSDIR	;POSITION IN DIRECTORY
L@00005:
	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JZ PRMPTR
	CMP AL,'!'	;LOGICAL CR?
	PUSHF
	INC BX
	POPF
	JZ PROMPT
	CALL UPCASE
	MOV  DUMTYP,AL	;TYPE OF DUMP (A,D,H)
;
;Command dispatcher
;
	CMP AL,'+'
	JNZ L@00008
	JMP PLUS
L@00008:
;
	CMP AL,'-'
	JNZ L@00009
	JMP MINUS
L@00009:
;
	CMP AL,'='
	JNZ L@00010
	JMP SEARCH
L@00010:
;
	CMP AL,'<'
	JNZ L@00011
	JMP SAVE
L@00011:
;
	CMP AL,'>'
	JNZ L@00012
	JMP RESTOR
L@00012:
;
	CMP AL,'#'
	JNZ L@00013
	JMP STATS
L@00013:
;
	CMP AL,'?'
	JNZ L@00014
	JMP HELP
L@00014:
;
	CMP AL,'A'
	JNZ L@00015
	JMP DUMP
L@00015:
;
	CMP AL,'C'
	JNZ L@00016
	JMP CHG
L@00016:
;
	CMP AL,'D'
	JNZ L@00017
	JMP DUMP
L@00017:
;
	CMP AL,'F'
	JNZ L@00018
	JMP POSFIL
L@00018:
;
	CMP AL,'G'
	JNZ L@00019
	JMP POS
L@00019:
;
	CMP AL,'H'
	JNZ L@00020
	JMP DUMP
L@00020:
;
	CMP AL,'L'
	JNZ L@00021
	JMP LOGIN
L@00021:
;
	CMP AL,'M'
	JNZ L@00022
	JMP MAP
L@00022:
;
	CMP AL,'N'
	JNZ L@00023
	JMP NEWDSK
L@00023:
;
	CMP AL,'P'
	JNZ L@00024
	JMP PRNTFF
L@00024:
;
	CMP AL,'Q'
	JNZ L@00025
	JMP QUIET
L@00025:
;
	CMP AL,'R'
	JNZ L@00026
	JMP DOREAD
L@00026:
;
	CMP AL,'S'
	JNZ L@00027
	JMP POS
L@00027:
;
	CMP AL,'T'
	JNZ L@00028
	JMP POS
L@00028:
;
	CMP AL,'U'	;******CP/M 2.x ONLY******
	JNZ L@00029
	JMP USER
L@00029:
;
	CMP AL,'V'
	JNZ L@00030
	JMP VIEW
L@00030:
;
	CMP AL,'W'
	JNZ L@00031
	JMP DORITE
L@00031:
;
	CMP AL,'X'
	JNZ L@00032
QUIT:
	CALL ILPRT 
	DB	'Confirm Quiting (Y/N)? ',0
	CALL CONIN
	CALL UPCASE
	CMP AL,'Y'
	JE EXIT
	  CALL CRLF
	  JMP PRMPTR
;
EXIT:
	MOV CL,0
	MOV DL,0
	JMP BDOS	;RETURN TO CP/M 86
        
L@00032:
;
	CMP AL,'Z'
	JNZ L@00033
	JMP SLEEP
L@00033:
;
	CMP AL,'/'
	JNZ L@00034
	JMP REPEAT
L@00034:
;
WHAT:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'?',0
	JMP PRMPTR
;
;Memory full error
;
MEMFUL:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'+++ Out of memory +++'
	DB	CR,LF,0
	JMP PRMPTR
;
;Print disk statistics
;
STATS:	PUSH BX
	CALL ILPRT
	DB	'Disk Information:',CR,LF
	DB	'Tracks:',9,9,0
	MOV BX,MAXTRK
	INC BX
	CALL DEC
	CALL ILPRT
	DB	CR,LF,'Sec/trk:',9,0
	MOV BX,SPT
	CALL DEC
	CALL ILPRT
	DB	CR,LF,'Grpsize:',9,0
	MOV AL,BLM
	INC AL
	MOV BL,AL
	MOV BH,0
	CALL DEC
	CALL ILPRT
	DB	' (sectors per group)',CR,LF
	DB	'Tot grps:',9,0
	MOV BX,DSM
	CALL DEC
	CALL ILPRT
	DB	CR,LF,'Dir entries:',9,0
	MOV BX,DRM
	INC BX
	CALL DEC
	CALL ILPRT
	DB	CR,LF,'Sys tracks:',9,0
	MOV BX,SYSTRK
	CALL DEC
	CALL CRLF
	POP BX
	JMP PROMPT
;
;The following command resets the disk
;system thru CP/M, and may be usable for
;changing the disk density or format.
;This can only be done if your BIOS resets
;the auto-density select parameters at
;every track-zero access.
;
NEWDSK:	PUSH BX
	MOV CL,RESETDK
	CALL BDOS
	MOV AL,DRIVE
	MOV CL,AL
	POP BX
	CALL SELECT
	JMP PROMPT
;
;Quite mode
;
QUIET:	MOV  QFLAG,AL	;NOW QUIET
	JMP PROMPT
;
;Repeat buffer contents
;
REPEAT:	CALL DECIN	;NN SPECIFIED?
	MOV AL,DH
	OR AL,DL
	JZ NNN		;NO.
	MOV BX,TOGO
	INC BX	;TEST FOR FIRST TIME
	MOV AL,BH
	OR AL,BL	;WAS IT 0FFFFH?
	JNZ NNN		;NO: COUNTING
	XCHG BX,DX	;GET COUNT
	MOV TOGO,BX	;SET COUNT
;
NNN:	MOV BX,TOGO
	XCHG BX,DX
	MOV BX,OFFSET INBUF	;READY TO REPEAT
	INC DX	;TEST FOR 0FFFFH
	MOV AL,DH
	OR AL,DL
	JNZ L@00037
	JMP PROMPT	;CONTINOUS
L@00037:
	DEC DX	;COUNT DOWN
	DEC DX	;MAKE UP FOR PREV INX D
	XCHG BX,DX
	MOV TOGO,BX
	MOV AL,BH	;ALL DONE?
	OR AL,BL
	XCHG BX,DX	;GET BACK INBUF PTR
	JZ L@00038
	JMP PROMPT	;NO, KEEP GOING
L@00038:
	JMP PRMPTR	;ALL DONE
;
;Set CP/M 2.x user number
;
USER:
	CALL DECIN	;GET REQUESTED USER NO.
	MOV AL,DL
	CMP AL,32	;VALID?
	JNAE L@00040
	JMP WHAT
L@00040:
	MOV AL,DH
	OR AL,AL
	JZ L@00041
	JMP WHAT
L@00041:
	MOV UNUM,DL	;SAVE IT
	MOV CL,SUSER
	PUSH BX	;SAVE CHAR POINTER
	CALL BDOS	;SET USER NO.
	POP BX
	JMP PROMPT
;
;Toggle print flag
;
PRNTFF:	MOV AL,PFLAG
	XOR AL,1
	MOV  PFLAG,AL
	JMP PROMPT
;
;Sleep routine, in tenths of a sec
;
SLEEP:	CALL HEXIN	;GET COUNT IF ANY
	MOV AL,DL	;ANY?
	OR AL,AL
	JNZ SLEPLP
	MOV DL,10
;
SLEPLP:	MOV CX,OFFSET 10000	
	MOV AL,CLOCK
	OR AL,AL
	JZ SLEEP2
	MOV CX,OFFSET 32000
;
SLEEP2:
	DEC CX
	MOV AL,CH
	OR AL,CL
	JNZ SLEEP2
	PUSH DX
	CALL CTLCS
	POP DX
	JNZ L@00045
	JMP PRMPTR
L@00045:
	DEC DL
	JNZ SLEPLP
	JMP PROMPT
;
;Check for control-C or S
;
CTLCS:	CALL CONST
	OR AL,AL
	JNZ GETC
	OR AL,1	;NO CHAR, RETN NZ
	RET
;
GETC:	CALL CONIN
	and al,5fh
	cmp al,'Q'	;a Q will quit
	jz quit_ret
	AND AL,1FH	;ALLOW ASCII
	CMP AL,'S'-40H
	JNZ L@00048
	CALL CONIN
L@00048:
	CMP AL,'['-40H  ;AN ESCAPE WILL QUIT
	JZ QUIT_RET
	CMP AL,'C'-40H  ;AND SO WILL ^C

QUIT_RET:
	RET	;0 SET IF CTL-C,esc
;
;Find our way at initialization
;
GETSTP:
	MOV	CL,SUSER	;GET USER NUMBER
	MOV	DL,0FFH		;GET USER
	CALL	BDOS
	MOV	UNUM,AL		;SET USER NUMBER

	MOV CL,GETDSK
	CALL BDOS	;GET CURNT DSK
	MOV CL,AL	;  WE HAVE TO SELECT
	JMP SELECT	;  TO GET THE DPH
;
LOGIN:	CALL DOLOG
	JMP PROMPT
;
DOLOG:	MOV AL,BYTE PTR [BX]	;DISK REQ?
	MOV DX,OFFSET 0
	CMP AL,CR
	JNZ L@00049
	JMP LGNODK
L@00049:
	CMP AL,'!'
	JNZ L@00050
	JMP LGNODK
L@00050:
	CALL UPCASE
	INC BX
	SUB AL,'A'
	MOV CL,AL
;
SELECT:	PUSH BX
	MOV AL,CL	;put drive in AL
	push cx		;save drive
	mov dl,0	;indicate first select	
	CALL SEL	;SELECT DISK THRU BIOS
	MOV AL,BH
	OR AL,BL
	pop cx
	JNZ L@00052
	JMP WHAT	;SELECT ERROR
L@00052:
	MOV  DRIVE,CL	;REMEMBER LATER WHERE WE ARE
;
	MOV AX,ES:[BX]	;GET THE SECTOR TABLE PTR
	MOV SECTBL,AX
	MOV AX,8		;IN VER 31 OFFSET IS 8
	CMP BYTE PTR CPM_VERSION,31H
	JAE AX8
	MOV AX,10		;BUT IN VER 11 IT IS 10 BYTES
AX8:	ADD BX,AX
	MOV BX,ES:[BX]	;GET DPB PTR
;
SELSKP:	CALL LOGIT
	MOV BX,SYSTRK	;RESET TRACK AND SECTOR
	XCHG BX,DX	;  TO DIRECTORY
	CALL SETTRK	;  ON EVERY
	MOV DX,OFFSET 1	;  LOGIN
	CALL SETSEC	;  CHANGE
	MOV BX,PHYSEC	;THIS LOGIC WILL TELL
	MOV AL,BH	;  IF FIRST SEC
	OR AL,BL	;  IS PHYSICAL 0
	MOV  FIRST0,AL
	cmp byte ptr cpm_version,31h ;if ver 31 then
	jb no_set_0		     ;use first sector=1
	mov first0,1
no_set_0:
	CALL CLCSUB
	POP BX
;
LGNODK:	CALL NORITE
	RET
;
;Read in the disk directory
;
REDDIR:	PUSH BX
	CALL NORITE	;POSITIONING LOST
	MOV BX,SYSTRK
	MOV CURTRK,BX
	MOV BX,OFFSET 1
	MOV CURSEC,BX
	MOV BX,DRM	;GET DIR SIZE FROM DPB
	INC BX	; MAKE 1-RELATIVE
	CALL ROTRHL
	CALL ROTRHL	;DIVIDE BY 4 (4 NAMES/SECTOR)
	MOV CX,BX
	MOV DX,OFFSET DIRECT	;DMA ADDR
;
RDIRLP:	PUSH CX
	PUSH DX
	MOV CX,DX
	MOV AL,0F0H	;FORCE MEMORY
	CMP AL,DH
	JAE L@00053
	JMP MEMFUL
L@00053:
	CALL SETDMA
	MOV BX,CURTRK
	XCHG BX,DX
	CALL SETTRK
	MOV BX,CURSEC
	XCHG BX,DX
	CALL SETSEC
	CALL READ
	CALL NXTSEC
	POP DX
	POP CX
	MOV BX,OFFSET 80H
	ADD BX,DX
	XCHG BX,DX
	DEC CX
	MOV AL,CH
	OR AL,CL
	JNZ RDIRLP
	MOV CX,OFFSET BASE +80H
	CALL SETDMA
	POP BX
	RET
;
;Map the directory
;
MAP:	CALL REDDIR	;READ IN DIRECTORY
	MOV CL,0	;INIT START GRP #
	MOV AL,AL0	;READ DIR GRP BITS
	CALL COLECT	;COLLECT COUNT OF DIR GRPS..
	MOV AL,AL1	;..IN REGISTER C
	CALL COLECT
	MOV CH,0	;BC NOW HAS A DEFAULT START GRP #
	CALL HEXIN
	PUSH BX	;SAVE INBUF PTR
	MOV AL,DL	;GET START
	OR AL,DH	;NOTHING?
	JZ MAPDF	;..YES, DFLT
	MOV CX,DX
;
MAPDF:	CALL HEXB
	MOV AL,'-'
	CALL TYPEOUT
	CALL GETGRP	;GET GRP(C) TO HL
;
MAPCNT:
	INC CX	;NEXT GRP #
	PUSH BX
	MOV BX,DSM	;GET HIGHEST GRP #
	INC BX	;PLUS 1 FOR COMPARISON
	MOV AL,BL	;WHEN BC REACHES DSM+1..
	CMP AL,CL	;..THEN WE HAVE EXCEEDED..
	JNZ MAPC1	;.. THE DISK CAPACITY..
	MOV AL,BH
	CMP AL,CH
;
MAPC1:	POP BX
	JZ MAPEND	;.. AND WE ARE DONE
	PUSH BX
	CALL GETGRP	;GET ANOTHER
	POP DX	;SEE IF SAME
	CALL CTLCS
	JZ MAPND2
	MOV AL,DH
	CMP AL,BH
	JNZ MAPDIF
	MOV AL,DL
	CMP AL,BL
	JZ MAPCNT	;SAME, CONTINUE
;
;Different file encountered
MAPDIF:
	DEC CX
	CALL HEXB
	INC CX
	XCHG BX,DX
	CALL MAPNAM
	JMP MAPDF
;
;End of map
;
MAPEND:
	DEC CX	;GET LAST
	CALL HEXB
	CALL MAPNAM
	POP BX
	CALL CRLF
;
;End of map - reposition to previous group
;
MAPND2:	PUSH BX
	MOV BX,GROUP
	XCHG BX,DX
	JMP POSGP2
;
;Print file name pointed to by HL
;
MAPNAM:	CALL SPACE
	MOV AL,BH
	OR AL,BL	;NONE?
	JZ NONAME
	MOV AL,BYTE PTR [BX]	;SEE IF ALLOC
;	cmp al,20h ! je noname
;	cmp al,21h ! je noname
	CMP AL,0E5H	;FREE?
	MOV AL,' '
	JNZ MPNSP1
	MOV AL,'['
;
MPNSP1:	CALL TYPEOUT
	PUSH BX	;SAVE POINTER
	MOV AL,BYTE PTR [BX]
	CALL HEX	;SHOW USER NUMBER
	CALL SPACE
	INC BX	;SKIP USER BYTE
	PUSH CX
	MOV CH,8
	CALL MAPN2
	MOV AL,'.'
	CALL TYPEOUT
	MOV CH,3
	CALL MAPN2
	POP CX
	CALL SPACE
	MOV AL,BYTE PTR [BX]	;GET EXT
	CALL HEX
	POP BX
	MOV AL,BYTE PTR [BX]
	CMP AL,0E5H
	MOV AL,' '
	JNZ MPNSP2
	MOV AL,']'
;
MPNSP2:	CALL TYPEOUT	;")" IF ERASED FILE
	JMP FLIP
;
NONAME:	CALL ILPRT
	DB	'    ++FREE++        ',0
;
FLIP:	MOV AL,TWOUP
	XOR AL,1
	MOV  TWOUP,AL
	JNZ L@00064
	JMP CRLF
L@00064:
;
DELIM:	MOV AL,':'
	CALL TYPEOUT
	JMP SPACE
;
;Print name, length in B
;
MAPN2:	MOV AL,BYTE PTR [BX]
	AND AL,7FH	;STRIP POSSIBLE 2.x ATTRIBUTE BIT
	INC BX
	CMP AL,' '	;PRINTABLE?
	JNAE MAPN2H	;..NO, IN HEX
	CMP AL,7EH	;7E IS LEADIN ON SOME CRTS
	JNAE MAPN2A
;
MAPN2H:	CALL BHEX
	JMP MAPN2Z
;
MAPN2A:	CALL TYPEOUT
;
MAPN2Z:	DEC CH
	JNZ MAPN2
	RET
;
;Find which file group (BC) belongs to
;
GETGRP:	MOV BX,DRM	;MAX DIR ENTRY #
	INC BX	;MAKE 1-RELATIVE
	MOV FILECT,BX
	MOV BX,OFFSET DIRECT
;
GETGLP:	PUSH BX	;SAVE POINTER TO NAME
	MOV AL,BYTE PTR [BX]	;PICK UP user number
	cmp al,20h ! je getgnf	;user must be <20h
	cmp al,21h ! je getgnf  ;
	MOV DX,OFFSET 14	;NOW GET RECORD COUNT
	ADD BX,DX	; S2 PORTION ..
	MOV AL,BYTE PTR [BX]	;  IS 0 IN CP/M 1.4
	CMP AL,0E5H
	JZ GETGNF
	AND AL,0FH
	MOV DL,AL
	INC BX
	MOV AL,BYTE PTR [BX]
	OR AL,DL
	JZ GETGNF
	MOV DL,16	;FIRST SET FOR 8-BIT GRPS
	MOV AL,BYTE PTR DSM+1
	OR AL,AL
	JZ SMALGP
	MOV DL,8	;NOPE, BIG GROUPS
;
SMALGP:	MOV DH,AL	;SAVE GRP SIZE INDICATOR
;
GETGL2:
	INC BX	;POINTING INTO DM FIELD
	CALL GRPCMP	;COMPARE BC GP # AGAINST 1 DM FLD
	JZ GETGOT	;JUMP IF FOUND ONE
	DEC DL	;ELSE COUNT DOWN
	JNZ GETGL2	;GO TEST SOME MORE
;
GETGNF:	POP BX	;NOT THIS ONE
	
	MOV DX,OFFSET 32	;SO GO TO NEXT
	ADD BX,DX
	XCHG BX,DX
	MOV BX,FILECT	;THERE IS LIMIT TO EVERYTHING
	DEC BX
	MOV FILECT,BX
	MOV AL,BH
	OR AL,BL
	XCHG BX,DX	;RE-ALIGN
	JNZ GETGLP
;
;Group is not allocated to any file
	MOV BX,OFFSET 0	;SAY SO
	RET
;
;Found the file
;
GETGOT:	POP BX
	RET
;
;Save the current sector
;
SAVE:	MOV AL,WRFLG
	OR AL,AL
	JNZ L@00074
	JMP BADW	;NONE TO SAVE
L@00074:
	PUSH BX
	MOV BX,OFFSET BASE +80H
	MOV DX,OFFSET SAVBUF
	MOV CH,128
	CALL MOVE
	MOV AL,1	;..SHOW
	MOV  SAVEFL,AL	;..SAVED EXISTS
	POP BX
	JMP PROMPT
;
;Restore the current sector
;
RESTOR:	MOV AL,SAVEFL
	OR AL,AL
	JZ NOSAVE	;NONE TO SAVE
	PUSH BX
	MOV BX,OFFSET SAVBUF
	MOV DX,OFFSET BASE +80H
	MOV CH,128
	CALL MOVE
	POP BX
	JMP PROMPT
;
NOSAVE:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++NO "<" SAVE COMMAND ISSUED'
	DB	CR,LF,0
	JMP PRMPTR
;
;Move (HL) to (DE) length in B
;
MOVE:	MOV AL,BYTE PTR [BX]
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX
	INC BX
	INC DX
	DEC CH
	JNZ MOVE
	RET
;
MOVEFROMBIOS:
	MOV AL,ES:BYTE PTR [BX]
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX
	INC BX
	INC DX
	DEC CH
	JNZ MOVEFROMBIOS
	RET
;
NORITE:	XOR AL,AL	;GET 0
	MOV  WRFLG,AL	;CAN'T WRITE NOW
	RET
;
;No match in search, try next char
;
SRNOMT:	POP BX
	CALL CTLCS	;ABORT?
	JNZ SEARCH	;...YES
	MOV BX,OFFSET INBUF
	MOV BYTE PTR [BX],CR
	JMP CLCGRP	;SHOW WHERE STOPPED
;
;Search for character string
;
SEARCH:	PUSH BX	;SAVE STRING POINTER
;
SRCHL:	CALL RDBYTE	;GET A BYTE
	MOV CH,AL	;SAVE IT
	MOV AL,BYTE PTR [BX]	;CHECK NEXT MATCH CHAR.
	CMP AL,'<'	;WILL IT BE HEX?
	MOV AL,CH	;RESTORE DISK CHAR
	JZ SRCHL1
	AND AL,7FH	;NEXT CHAR IS ASCII...STRIP BIT 7
;
SRCHL1:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	CALL GETVAL	;GET SEARCH VALUE
	MOV CH,AL
	POP AX
	XCHG AL,AH
	SAHF
	CMP AL,CH	;MATCH?
	JNZ SRNOMT	;NO MATCH
	INC BX
	MOV AL,BYTE PTR [BX]	;DONE?
	CMP AL,CR
	JZ SREQU
	CMP AL,'!'
	JNZ SRCHL
;
;Got match
SREQU:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'= AT ',0
	MOV AL,BYTE PTR BUFAD
	AND AL,7FH
	CALL HEX
	CALL CRLF
	JMP CLCGRP
;
;Get value from input buffer
;
GETVAL:	MOV AL,BYTE PTR [BX]
	CMP AL,'<'	;HEX ESCAPE?
	JZ L@00082
	RET	;NO, RETURN
L@00082:
;"<<" means one "<"
	INC BX
	MOV AL,BYTE PTR [BX]
	CMP AL,'<'
	JNZ L@00083
	RET
L@00083:
;Got hex
	PUSH DX
	CALL HEXIN	;GET VALUE
	CMP AL,'>'	;PROPER DELIM?
	MOV AL,DL	;GET VALUE
	POP DX
	JZ L@00084
	JMP WHAT	;ERROR
L@00084:
	RET
;
;Read a byte at a time
;
RDBYTE:	PUSH BX
	MOV AL,FTSW	;FIRST READ?
	OR AL,AL
	JNZ READ1
	MOV BX,BUFAD
	MOV AL,BL
	OR AL,AL	;IN BUFFER?
	JS NORD		;YES, SKIP READ
;
;Have to read
	CALL NXTSEC
;
READ1:	XOR AL,AL
	MOV  FTSW,AL	;NOT FIRST READ
	MOV BX,CURSEC
	XCHG BX,DX
	CALL SETSEC
	MOV BX,CURTRK
	XCHG BX,DX
	CALL SETTRK
	CALL READ
	CALL CLCSUB
	MOV BX,OFFSET BASE +80H
;
NORD:	MOV AL,BYTE PTR [BX]
	INC BX
	MOV BUFAD,BX
	POP BX
	RET
;
;View the file in ASCII starting at
;current sector, stepping thru the disk
;
VIEW:	MOV AL,WRFLG
	OR AL,AL
	JNZ L@00087
	JMP BADDMP
L@00087:
	CALL HEXIN	;GET DISPL IF ANY
	PUSH BX
	MOV AL,DL
	OR AL,AL
	JNZ VIEWLP
	INC DL	;DFLT=1
;
VIEWLP:	MOV BX,OFFSET BASE +80H	;TO DATA
;
VEWCHR:	CALL CTLCS
	JZ VEWEND
	MOV AL,BYTE PTR [BX]
	CMP AL,1AH
	JZ VEWEOF
	AND AL,7FH
	CMP AL,7EH
	JAE VIEWHX	;SHOW RUBOUT AND TILDE AS HEX
	CMP AL,' '
	JAE VIEWPR
	CMP AL,CR
	JZ VIEWPR
	CMP AL,LF
	JZ VIEWPR
	CMP AL,TAB
	JZ VIEWPR
;
VIEWHX:	MOV AL,BYTE PTR [BX]	;NOT ASCII...PRINT AS <NN>
	CALL BHEX
	JMP VIEWNP
;
VIEWPR:	CALL TYPEOUT
;
VIEWNP:	INC BL
	JNZ VEWCHR
	DEC DL
	JZ VEWEND
	PUSH DX	;SAVE COUNT
	CALL NXTSEC
	MOV BX,CURSEC
	XCHG BX,DX
	CALL SETSEC
	MOV BX,CURTRK
	XCHG BX,DX
	CALL SETTRK
	CALL READ
	POP DX	;RESTORE COUNT
	JMP VIEWLP
;
VEWEOF:	CALL ILPRT
	DB	CR,LF,TAB,'++EOF++',CR,LF,0
;
VEWEND:	POP BX
	CALL CRLF
	JMP CLCGRP
;
;Dump in hex or ASCII
;
DUMP:	MOV AL,WRFLG
	OR AL,AL
	JNZ DUMPOK
;
BADDMP:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++Can''t dump, no sector read.',CR,LF,0
;
EXPL:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'Use G command following F,',CR,LF
	DB	'or R or S following T',CR,LF,0
	JMP PRMPTR
;
DUMPOK:	MOV AL,BYTE PTR [BX]
	CMP AL,'!'
	JZ DUMPDF	;DFLT
	CMP AL,CR
	JNZ DMPNDF
;
;Use default
DUMPDF:	MOV CX,OFFSET BASE +80H
	MOV DX,OFFSET 0FFH
	JMP DUMP1
;
DMPNDF:	CALL DISP
	MOV CX,DX
	CMP AL,CR
	JZ DUMP1
	CMP AL,'!'
	JZ DUMP1
	INC BX	;SKIP ','
	CALL DISP
;
;BC = start, DE = end
;
DUMP1:	PUSH BX	;SAVE COMMAND POINTER
	MOV BX,CX
;
DUMPLP:	MOV AL,BL
	AND AL,7FH
	CALL HEX
	CALL SPACE
	CALL SPACE
	MOV AL,DUMTYP
	CMP AL,'A'
	JZ DUMPAS
	PUSH BX	;SAVE START
;
DHEX:	MOV AL,BYTE PTR [BX]
	CALL HEX
	MOV AL,BL
	AND AL,3
	CMP AL,3
	JNZ L@00104
	CALL SPACE
L@00104:
	MOV AL,BL
	AND AL,7
	CMP AL,7
	JNZ L@00105
	CALL SPACE
L@00105:
	MOV AL,DL
	CMP AL,BL
	JZ DPOP
	INC BX
	MOV AL,BL
	AND AL,0FH
	JNZ DHEX
;
DPOP:	CALL CTLCS
	JNZ L@00108
	JMP PRMPTR
L@00108:
	MOV AL,DUMTYP
	CMP AL,'H'
	JZ DNOAS	;HEX ONLY
	POP BX	;GET START ADDR
;
DUMPAS:	CALL ASTER
;
DCHR:	MOV AL,BYTE PTR [BX]
	AND AL,7FH
	CMP AL,' '
	JNAE DPER
	CMP AL,7EH
	JNAE DOK
;
DPER:	MOV AL,'.'
;
DOK:	CALL TYPEOUT
	MOV AL,DL
	CMP AL,BL
	JZ DEND
	INC BX
	MOV AL,BL
	AND AL,0FH
	JNZ DCHR
;
DEND:	CALL ASTER
	CALL CRLF
	PUSH DX
	CALL CTLCS
	POP DX
	JNZ L@00114
	JMP PRMPTR
L@00114:
	MOV AL,DL
	CMP AL,BL
	JZ L@00115
	JMP DUMPLP
L@00115:
	POP BX
	JMP PROMPT
;
DNOAS:	POP CX
	CALL CRLF
	MOV AL,DL
	CMP AL,BL
	JZ L@00116
	JMP DUMPLP
L@00116:
	POP BX
	JMP PROMPT
;
;Position
;
POS:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	MOV AL,BYTE PTR [BX]
	CMP AL,'!'
	JZ POSINQ
	CMP AL,CR
	JNZ POSOK
;
POSINQ:	POP AX
	XCHG AL,AH
	SAHF
	JMP INQ
;
POSOK:	POP AX
	XCHG AL,AH
	SAHF
	CMP AL,'T'
	JZ POSTKD
	CMP AL,'S'
	JZ POSSCD
	CMP AL,'G'
	JNZ L@00121
	JMP POSGPH
L@00121:
	JMP WHAT
;
POSTKD:	CALL DECIN
;
POSTRK:	PUSH BX
	MOV BX,MAXTRK
	CALL SUBDE
	POP BX
	JAE L@00122
	JMP OUTLIM
L@00122:
	CALL SETTRK
	CALL NORITE	;TRACK DOESN'T READ
	MOV AL,1
	MOV  NOTPOS,AL	;SHOW NOT POSITIONED
	JMP CLCGRP
;
POSSCD:	CALL DECIN
	MOV AL,DH
	OR AL,DL
	JNZ L@00123
	JMP WHAT	;DON'T ALLOW SECTOR 0
L@00123:
;
POSSEC:	PUSH BX
	MOV BX,SPT
	CALL SUBDE
	POP BX
	JAE L@00124
	JMP WHAT
L@00124:
	CALL SETSEC
	CALL READ
	XOR AL,AL
	MOV  NOTPOS,AL	;POSITIONED OK
;
CLCGRP:	CALL CLCSUB
	JMP INQ
;
;Calculate group from track and sector
;
CLCSUB: push bx
	mov ax,CURTRK
	mov bx,SYSTRK
	cmp bx,ax ! ja a_systrack
	sub ax,SYSTRK
a_systrack:
	xor dx,dx
	mul SPT		;TRK*SPT=hi bits in DX lo bits in AX
	mov bx,CURSEC
	dec bx
	add ax,bx	;add in cursec - 1
	adc dx,0
divide1:
	mov bx,1
	mov cl,BSH
	shl bx,cl	;BX = 2^BSH
	div bx		;DX:AX=((CURTRK-SYSTRK)*SPT+CURSEC-1)/(2^BSH)

	mov group,ax	;AX=quotent, DX=remainder
	mov GRPDIS,dl
	pop bx
	ret
;
;Position in the dorectory after a find
;(Does not work in CP/M-2.x)
;
POSDIR:	PUSH BX	;SAVE INBUF
	MOV BX,WORD PTR BSH
	XOR AL,AL
	MOV  FINDFL,AL	;CANCEL POS REQ
	MOV AL,DIRPOS	;GET POSITION
	RCR AL,1
	RCR AL,1
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	AND AL,BH
	MOV  GRPDIS,AL
	POP AX
	XCHG AL,AH
	SAHF
;
POSDLP:	RCR AL,1
	DEC BL
	JNZ POSDLP
	AND AL,1	;GET GROUP
	MOV BL,AL	;SETUP FOR POSGP2
	MOV BH,0
	MOV GROUP,BX
	XCHG BX,DX
	JMP POSGP2	;POSITION TO IT
;
POSGPH:	CALL HEXIN
;
POSGRP:	PUSH BX
	MOV BX,DSM
	CALL SUBDE
	POP BX
	JAE L@00127
	JMP OUTLIM
L@00127:
	XCHG BX,DX
	MOV GROUP,BX
	XCHG BX,DX
	XOR AL,AL
	MOV  GRPDIS,AL
	PUSH BX
;
POSGP2:	CALL GTKSEC
	CALL SETTRK
	XCHG BX,DX
	inc  dx
	CALL SETSEC
	CALL READ
	XOR AL,AL
	MOV  NOTPOS,AL	;NOW POSITIONED
	POP BX
	JMP INQ
;
GTKSEC:
	MOV BX,DX	;BX = number of blocks
	mov cl,BSH
	
	mov ax,1
	shl ax,cl	;AX=2^BSH
	xor dx,dx	;clear DX register
	mul bx		;AX*BX=hi 16 bits in DX, lo in AX
	xor bx,bx
	mov bl,GRPDIS
	add ax,bx	;add in GRPDIS
	adc dx,0	;with carry into DX
;
;Divide by nbr of sectors, quotient=track, remainder=sector
;NOTE for O/S = ver 3.1+, SPT is in 128 byte units, not phy sectors
;
divide2:
	div Word Ptr SPT ;DX:AX/SPT=AX(quotent)=track, DX(remainder)=sector
	add ax,SYSTRK	;add in track offset
	mov bx,dx	;and get stuff into proper registers
	mov dx,ax	;for the return
	or bx,bx	;don't allow a zero sector number
	ret
;
POSFIL:	CALL NORITE
	MOV AL,1
	MOV  FINDFL,AL	;SO WE POSITION LATER
	MOV DX,OFFSET FCB
	XOR AL,AL	;LOGGED IN DISK
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX
	INC DX
	MOV CH,8
	CALL MVNAME
	MOV CH,3
	CALL MVNAME
	MOV DX,OFFSET FCB
	MOV CL,SRCHF
	PUSH BX
	CALL BDOS
	INC AL
	JNZ FLOK
	MOV  DIRPOS,AL	;GRP 0 IF NOT FOUND
	CALL ILPRT
	DB	'++FILE NOT FOUND',CR,LF,0
	POP BX
	JMP PROMPT
;
FLOK:	DEC AL
	MOV  DIRPOS,AL	;SAVE POS. IN DIR
	AND AL,3
	MOV BL,AL
	MOV BH,0
	ADD BX,BX
	ADD BX,BX
	ADD BX,BX
	ADD BX,BX
	ADD BX,BX
	MOV DX,OFFSET BASE +80H
	ADD BX,DX
	MOV DX,OFFSET 32
	XCHG BX,DX
	ADD BX,DX
	XCHG BX,DX
	MOV AL,'D'
	MOV  DUMTYP,AL
	JMP DUMPLP	;WHICH POPS H
;
MVNAME:	MOV AL,BYTE PTR [BX]
	CMP AL,'.'
	JZ MVIPAD
	CMP AL,CR
	JZ PAD
	CMP AL,'!'
	JZ PAD
	CALL UPCASE
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX
	INC BX
	INC DX
	DEC CH
	JNZ MVNAME
	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JNZ L@00135
	RET
L@00135:
	CMP AL,'!'
	JNZ L@00136
	RET
L@00136:
	INC BX
	CMP AL,'.'
	JNZ L@00137
	RET
L@00137:
	JMP WHAT
;
MVIPAD:
	INC BX
;
PAD:	MOV AL,' '
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX
	INC DX
	DEC CH
	JNZ PAD
	RET
;
PLUS:	MOV DX,OFFSET 1	;DFLT TO 1 SECT
	MOV AL,BYTE PTR [BX]	;GET NEXT CHAR
	CMP AL,CR	;CR?
	JZ PLUSGO	;.. YES, DFLT TO 1
	CMP AL,'!'
	JZ PLUSGO
	CALL HEXIN	;GET #
	MOV AL,DH
	OR AL,DL
	JNZ L@00141
	JMP WHAT
L@00141:
;
PLUSGO:	CALL NXTSEC
	DEC DX	;MORE TO GO?
	MOV AL,DH
	OR AL,DL
	JNZ PLUSGO	;..YES
;
;Ok, incremented to sector.  Setup and read
;
PLUSMI:	PUSH BX
	MOV BX,CURSEC
	XCHG BX,DX
	CALL SETSEC
	MOV BX,CURTRK
	XCHG BX,DX
	CALL SETTRK
	POP BX
	CALL READ
	JMP CLCGRP
;
MINUS:	MOV DX,OFFSET 1	;SET DFLT
	MOV AL,BYTE PTR [BX]	;GET CHAR
	CMP AL,CR	;CR?
	JZ MINGO	;.. YES, DFLT=1
	CMP AL,'!'
	JZ MINGO
	CALL HEXIN	;..NO, GET ##
	MOV AL,DH
	OR AL,DL
	JNZ L@00145
	JMP WHAT
L@00145:
;
MINGO:	PUSH BX
	MOV BX,CURSEC
	DEC BX
	MOV AL,BH
	OR AL,BL
	JNZ MINOK
	MOV BX,CURTRK
	MOV AL,BH
	OR AL,BL
	JNZ SEASH
	MOV BX,MAXTRK	;WRAP TO END OF DISK
	MOV CURTRK,BX
	MOV BX,MAXSEC
	JMP MINOK
;
SEASH:
	DEC BX
	MOV CURTRK,BX
	MOV BX,SPT
;
MINOK:	MOV CURSEC,BX
	POP BX
	DEC DX
	MOV AL,DH
	OR AL,DL
	JNZ MINGO
	JMP PLUSMI
;
;Go to next sector
;
NXTSEC:	PUSH BX
	PUSH DX
	MOV BX,CURSEC
	INC BX
	XCHG BX,DX
	MOV BX,SPT
	CALL SUBDE
	XCHG BX,DX
	JAE NEXTOK
	MOV BX,CURTRK
	INC BX
	XCHG BX,DX
	MOV BX,MAXTRK
	CALL SUBDE
	JAE TRASK
	MOV DX,OFFSET 0	;WRAP TO START OF DISK
;
TRASK:	XCHG BX,DX
	MOV CURTRK,BX
	MOV BX,OFFSET 1
;
NEXTOK:	MOV CURSEC,BX
	POP DX
	POP BX
	RET
;
;Tell what group, displacement, track, sector, physical sector
;
INQ:	CALL INQSUB
	JMP PROMPT
;
;Position inquiry subroutine
;Executed via: G S or T (with no operands)
;
INQSUB:	PUSH BX
	MOV BX,SYSTRK
	XCHG BX,DX
	MOV BX,CURTRK
	CALL SUBDE
	JNAE NOGRP
	CALL ILPRT
	DB	'G=',0
	MOV BX,GROUP
	MOV CH,BH
	MOV CL,BL
	CALL HEXB
	MOV AL,':'
	CALL TYPEOUT
	MOV AL,GRPDIS
	CALL HEX
	MOV AL,','
	CALL TYPEOUT
;
NOGRP:	CALL ILPRT
	DB	' T=',0
	MOV BX,CURTRK
	CALL DEC
	CALL ILPRT
	DB	', S=',0
	MOV BX,CURSEC
	CALL DEC
	CALL ILPRT
	DB	', PS=',0
	MOV BX,PHYSEC
	CALL DEC
	CALL CRLF
	POP BX
	RET
;
CHG:	MOV AL,BYTE PTR [BX]	;GET TYPE (HEX, ASCII)
	CALL UPCASE
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE "H" OR "A"
	INC BX
	CALL DISP	;GET, VALIDATE DISP TO DE
	INC BX
	MOV CX,OFFSET 0	;SHOW NO 'THRU' ADDR
	CMP AL,'-'	;TEST DELIM FR. DISP
	JNZ CHGNTH	;NO THRU
	PUSH DX	;SAVE FROM
	CALL DISP	;GET THRU
	INC BX	;SKIP END DELIM
	MOV CX,DX	;BC = THRU
	POP DX	;GET FROM
	JMP CHGAH
;
CHGNTH:	CMP AL,','
	JZ L@00153
	JMP WHAT
L@00153:
;
CHGAH:	POP AX
	XCHG AL,AH
	SAHF
	CMP AL,'H'
	JNZ L@00154
	JMP CHGHEX
L@00154:
	CMP AL,'A'
	JZ L@00155
	JMP WHAT
L@00155:
;
;Change ASCII
CHGALP:	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JNZ L@00156
	JMP PROMPT
L@00156:
	CMP AL,'!'
	JNZ L@00157
	JMP PROMPT
L@00157:
	XCHG BX,DX
	MOV AL,[BX]
	XCHG BX,DX
	CMP AL,' '
	JNAE CHGAHX
	CMP AL,7EH
	JAE CHGAHX
	JMP CHGA2
;
CHGAHX:	CALL BHEX
	JMP CHGA3
;
CHGA2:	CALL TYPEOUT
;
CHGA3:	MOV BACK,BX	;IN CASE "THRU"
	CALL GETVAL	;ASCII OR <HEX>
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX	;UPDATE CHAR
	INC BX	;TO NEXT INPUT CHAR
;See if 'THRU' requested
	MOV AL,CL
	OR AL,AL
	JZ CHANTH
	CMP AL,DL	;DONE?..
	JNZ L@00161
	JMP PROMPT	;..YES
L@00161:
	MOV BX,BACK
;
CHANTH:	INC DL
	JZ L@00162
	JMP CHGALP
L@00162:
	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JNZ L@00163
	JMP PROMPT
L@00163:
	CMP AL,'!'
	JNZ L@00164
	JMP PROMPT
L@00164:
	JMP WHAT
;
;Change hex
;
CHGHCM:
	INC BX
;
CHGHEX:	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JNZ L@00165
	JMP PROMPT
L@00165:
	CMP AL,'!'
	JNZ L@00166
	JMP PROMPT
L@00166:
	CMP AL,','	;DELIM?
	JZ CHGHCM
	PUSH DX
	MOV HEXAD,BX	;IN CASE 'THRU'
	CALL HEXIN	;POSITIONS TO DELIM
	MOV AL,DL	;GET VALUE
	POP DX	;..ADDR
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE VALUE
	XCHG BX,DX
	MOV AL,[BX]
	XCHG BX,DX	;GET OLD
	CALL HEX	;ECHO IN HEX
	POP AX
	XCHG AL,AH
	SAHF	;GET NEW
	XCHG BX,DX
	MOV [BX],AL
	XCHG BX,DX	;SAVE NEW
	MOV AL,CL	;SEE IF 'THRU'
	OR AL,AL
	JZ CHHNTH	;..NO.
	CMP AL,DL	;..YES, DONE?
	JNZ L@00169
	JMP PROMPT
L@00169:
	MOV BX,HEXAD	;..NO: MORE
;
CHHNTH:	INC DL
	JNZ CHGHEX
	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JNZ L@00171
	JMP PROMPT
L@00171:
	CMP AL,'!'
	JNZ L@00172
	JMP PROMPT
L@00172:
	JMP WHAT
;
DOREAD:	MOV AL,NOTPOS
	OR AL,AL
	JNZ CANTRD
	CALL READ
	JMP PROMPT
;
CANTRD:	XOR AL,AL
	MOV  QFLAG,AL	;NOT QUIET
	CALL ILPRT
	DB	'++Can''t read - not positioned',CR,LF
	DB	'Position by:',CR,LF
	DB	9,'Track then Sector, or',CR,LF
	DB	9,'Group',CR,LF,0
	JMP PROMPT
;
DORITE:	CALL WRITE
	JMP PROMPT
;
BHEX:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	MOV AL,'<'
	CALL TYPEOUT
	POP AX
	XCHG AL,AH
	SAHF
	CALL HEX
	MOV AL,'>'
	CALL TYPEOUT
	RET
;
HEXB:	MOV AL,BYTE PTR DSM+1
	OR AL,AL
	JZ HEXX
	MOV AL,CH
	CALL HEX
;
HEXX:	MOV AL,CL
;
HEX:	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH
	RCR AL,1
	RCR AL,1
	RCR AL,1
	RCR AL,1
	CALL NIBBL
	POP AX
	XCHG AL,AH
	SAHF
;
NIBBL:	AND AL,0FH
	CMP AL,10
	JNAE HEXNU
	ADD AL,7
;
HEXNU:	ADD AL,'0'
	JMP TYPEOUT
;
;Decimal output routine
;
DEC:	PUSH CX
	PUSH DX
	PUSH BX
	MOV CX,-OFFSET 10
	MOV DX,-OFFSET 1
;
DECOU2:	PUSHF
	ADD BX,CX
	RCR SI,1
	POPF
	RCL SI,1
	PUSHF
	INC DX
	POPF
	JNAE DECOU2
	MOV CX,OFFSET 10
	ADD BX,CX
	XCHG BX,DX
	MOV AL,BH
	OR AL,BL
	JZ L@00177
	CALL DEC
L@00177:
	MOV AL,DL
	ADD AL,'0'
	CALL TYPEOUT
	POP BX
	POP DX
	POP CX
	RET
;
SPACE:	MOV AL,' '
	JMP TYPEOUT
;
ASTER:	MOV AL,'*'
	JMP TYPEOUT
;
;Inline print routine
;
ILPRT:	POP SI
	XCHG BX,SI
	PUSH SI
;
ILPLP:	CALL CTLCS	;ABORT?
	JNZ L@00178
	JMP PRMPTR
L@00178:
	MOV AL,BYTE PTR [BX]
	CMP AL,1	;PAUSE?
	JNZ ILPOK
	CALL CONIN
	CMP AL,1bh ! je il_abort	;escape
	CMP AL,'Q'-40h ! je il_abort	;^Q
	CMP AL,'C'-40h ! je il_abort	;^C=ABORT?
 	JMPS L@00180
IL_ABORT:
	JMP PRMPTR
L@00180:
	JMP ILPNX
;
ILPOK:	CALL TYPEOUT
;
ILPNX:
	INC BX
	MOV AL,BYTE PTR [BX]
	OR AL,AL
	JNZ ILPLP
	INC BX
	POP SI
	XCHG BX,SI
	PUSH SI
	RET
;
;DISP calls HEXIN, and validates a sector
;displacement, then converts it to an address
;
DISP:	CALL HEXIN
	LAHF
	XCHG AL,AH
	PUSH AX
	XCHG AL,AH	;SAVE DELIMITER
	MOV AL,DH
	OR AL,AL
	JNZ BADISP
	MOV AL,DL
	OR AL,AL
	JS BADISP
	ADD AL,80H	;TO POINT TO BUFFER AT BASE+80H
	MOV DL,AL
	MOV DH,BASE/256
	POP AX
	XCHG AL,AH
	SAHF	;GET DELIM
	RET
;
BADISP:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++BAD DISPLACEMENT (NOT 0-7F)'
	DB	CR,LF,0
	JMP PRMPTR
;
HEXIN:	MOV DX,OFFSET 0
	MOV AL,BYTE PTR [BX]
	CMP AL,'#'	;DECIMAL?
	JZ HDIN		;MAKE DECIMAL
;
HINLP:	MOV AL,BYTE PTR [BX]
	CALL UPCASE
	CMP AL,CR
	JNZ L@00185
	RET
L@00185:
	CMP AL,'!'
	JNZ L@00186
	RET
L@00186:
	CMP AL,','
	JNZ L@00187
	RET
L@00187:
	CMP AL,'-'	;'THRU'?
	JNZ L@00188
	RET
L@00188:
	CMP AL,'>'
	JNZ L@00189
	RET
L@00189:
	INC BX
	CMP AL,'0'
	JAE L@00190
	JMP WHAT
L@00190:
	CMP AL,'9'+1
	JNAE HINNUM
	CMP AL,'A'
	JAE L@00192
	JMP WHAT
L@00192:
	CMP AL,'F'+1
	JNAE L@00193
	JMP WHAT
L@00193:
	SUB AL,7
;
HINNUM:	SUB AL,'0'
	XCHG BX,DX
	ADD BX,BX
	ADD BX,BX
	ADD BX,BX
	ADD BX,BX
	ADD AL,BL
	MOV BL,AL
	XCHG BX,DX
	JMP HINLP
;
HDIN:
	INC BX	;SKIP '.'
;
DECIN:	MOV DX,OFFSET 0
;
DINLP:	MOV AL,BYTE PTR [BX]
	CALL UPCASE
	CMP AL,CR
	JNZ L@00194
	RET
L@00194:
	CMP AL,'!'
	JNZ L@00195
	RET
L@00195:
	CMP AL,','
	JNZ L@00196
	RET
L@00196:
	CMP AL,'-'	;'THRU'?
	JNZ L@00197
	RET
L@00197:
	INC BX
	CMP AL,'0'
	JAE L@00198
	JMP WHAT
L@00198:
	CMP AL,'9'+1
	JNAE L@00199
	JMP WHAT
L@00199:
	SUB AL,'0'
	PUSH BX
	MOV BH,DH
	MOV BL,DL
	ADD BX,BX
	ADD BX,BX
	ADD BX,DX
	ADD BX,BX
	ADD AL,BL
	MOV BL,AL
	MOV AL,BH
	ADC AL,0
	MOV BH,AL
	XCHG BX,DX
	POP BX
	JMP DINLP
;
;Read in a console buffer full
;
RDBUF:	;PRINT PROMPT AS DU nnA:
	CALL	ILPRT
	DB	CR,LF,'DU ',0 	;SAY WHO WE ARE
	MOV 	AL,UNUM
	CMP 	AL,0 ! JZ DONT_0
	MOV	BL,AL   	;DISPLAY USER NUMBER
	MOV	BH,0
	CALL	DEC		;PRINT IN DECIMAL
DONT_0:
	MOV	AL,DRIVE	;GET DRIVE NUMBER
	ADD	AL,'A'		;CONVERT TO ASCII
	CALL	TYPEOUT
	CALL	ILPRT		;PRINT THE PROMPT
	DB	': ',0	
;
RDBF1:	MOV BX,OFFSET INBUF
	MOV CH,0
;
RDBLP:	CALL CONIN
	MOV CL,AL	;SAVE FOR BS TEST
;
;Evaluate control characters
;
	CMP AL,'U'-40H
	JNZ L@00200
	JMP RDCTLU
L@00200:
;
	CMP AL,CR
	JZ RDCR
;
	CMP AL,'H'-40H
	JZ RDBS
;
	CMP AL,7FH
	JZ RDBS
;
	CMP AL,'R'-40H
	JZ RDCTLR
;
	CMP AL,'X'-40H
	JZ RDCTLX

	CMP AL,'['-40H
	JNE NO_QUIT
	  JMP QUIT
NO_QUIT:
;
	CMP AL,' '
	JNAE RDBLP
;
	MOV BYTE PTR [BX],AL
	INC BX
	INC CH
	JS FULL
	CALL TYPEOUT
	JMP RDBLP
;
FULL:	DEC CH
	PUSHF
	DEC BX
	POPF
	MOV AL,'*'	;SIGNAL WE'RE FULL
	CALL TYPEOUT
	JMP RDBLP
;
;Got CR
;
RDCR:	MOV BYTE PTR [BX],AL	;SAVE IT
	CALL TYPEOUT	;ECHO IT
	MOV AL,LF	;ECHO..
	CALL TYPEOUT	;..LF
	MOV BX,OFFSET INBUF
	RET
;
;Got DELETE or BS, echo if BS
;
RDBS:	XOR AL,AL	;AT FRONT..
	OR AL,CH	;..OF LINE?
	JZ RDCTLU	;.. YES, ECHO ^U
	DEC BX
	DEC CH
	MOV AL,CL
	CMP AL,'H'-40H	;BS?
	JZ BACKUP	;ECHO THE BS
	MOV AL,BYTE PTR [BX]	;ECHO..
	CALL TYPEOUT	;..DELETED CHAR
	JMP RDBLP
;
BACKUP:	CALL WIPER
	JMP RDBLP
;
RDCTLX:	INC CH
;
RDCX1:	DEC CH
	JZ RDBF1
	CALL WIPER
	JMP RDCX1
;
WIPER:	PUSH CX
	PUSH DX
	PUSH BX
	MOV DX,OFFSET BSMSG	;BACKSPACE, SPACE, BACKSPACE
	MOV CL,PRINT
	CALL BDOS
	POP BX
	POP DX
	POP CX
	RET
;
BSMSG	DB	BS,' ',BS,'$'
;
;Got CTL-R, retype
;
RDCTLR:	MOV BYTE PTR [BX],CR
	CALL CRLF
	MOV BX,OFFSET INBUF
	MOV CH,0
;
RDCRL:	MOV AL,BYTE PTR [BX]
	CMP AL,CR
	JNZ L@00211
	JMP RDBLP
L@00211:
	CALL TYPEOUT
	INC CH
	INC BX
	JMP RDCRL
;
;Got CTL-U or backup to beginning of line.
;
RDCTLU:	MOV AL,'^'
	CALL TYPEOUT
	MOV AL,'U'
	CALL TYPEOUT
	JMP RDBUF
;
CRLF:	MOV AL,CR
	CALL TYPEOUT
	MOV AL,LF
	JMP TYPEOUT
;
UPCASE:	CMP AL,60H
	JNB L@00212
	RET
L@00212:
	AND AL,5FH	;MAKE UPPER CASE
	RET
;
CONST:	PUSH CX
	PUSH DX
	PUSH BX
	CALL BCONST	;GET CONSOLE STATUS USING BIOS CALL
	POP BX
	POP DX
	POP CX
	RET
;
CONIN:	PUSH CX
	PUSH DX
	PUSH BX
	CALL BCONIN	;GET CONSOLE CHAR FROM BIOS
	POP BX
	POP DX
	POP CX
	RET
;
;Console out with TAB expansion
;	Enter: char in AL
;
TYPEOUT:
	PUSH CX
	PUSH DX
	PUSH BX
	MOV CL,AL	;FOR OUTPUT ROUTINE
	CMP AL,TAB
	JNZ TYPE2
;
TYPTAB:	MOV AL,' '
	CALL TYPEOUT
	MOV AL,TABCOL
	AND AL,7
	JNZ TYPTAB
	JMP TYPRET
;
;Filter out control characters to
;prevent garbage during view of file
;
TYPE2:	CMP AL,' '
	JAE TYPEQ
	CMP AL,CR
	JZ TYPEQ
	CMP AL,LF
	JNZ TYPNCR
;
TYPEQ:	MOV AL,QFLAG
	OR AL,AL

VCONOT:	JNZ L@00218
	PUSH CX
	CALL BCONOUT	;CONSOLE OUT THRU BIOS
	POP CX
L@00218:
;
;Update column used in tab expansion
	MOV AL,CL	;GET CHAR
	CMP AL,CR
	JNZ TYPNCR
	MOV AL,0
	MOV  TABCOL,AL
	JMP TYPLST
;
TYPNCR:	CMP AL,' '	;CTL CHAR?
	JNAE TYPLST	;..NO CGANGE IN COL
	MOV AL,TABCOL
	INC AL
	MOV  TABCOL,AL
;
TYPLST:	MOV AL,PFLAG
	AND AL,1
	JZ L@00221
	CALL LISTOUT	;FROM C REG.
L@00221:
;
TYPRET:	POP BX
	POP DX
	POP CX
	RET
;
LISTOUT: ;enter char in CL		
	mov dl,cl	;put char in DL
	mov cl,L_WRITE	;write to default list device
	CALL BDOS	;LIST TO PRINTER THRU BDOS
	RET
;
HOME:	PUSH BX
	CALL BHOME	;HOME DRIVE THRU BIOS
	POP BX
	RET
;
;Set track # in DE
;
SETTRK:	PUSH BX
	MOV BX,MAXTRK
	CALL SUBDE
	POP BX
	JNAE OUTLIM
	XCHG BX,DX
	MOV CURTRK,BX
	XCHG BX,DX
	MOV CX,DX
	PUSH BX
	CALL TRK	;SET TRACK THRU BIOS
	POP BX
	RET
;
SETSEC:	PUSH BX
	PUSH DX
	MOV BX,SYSTRK
	XCHG BX,DX
	MOV CURSEC,BX
	MOV BX,CURTRK
	CALL SUBDE
	POP CX
	MOV BX,CX
	JAE NOTSYS
	MOV AL,FIRST0	;SEE IF FIRST SEC 0
	OR AL,AL
	JNZ GSTSEC	;NO, JUMP AWAY
	DEC BX	;YES, SO DECREMENT
	JMP GSTSEC	;  REQUESTED, THEN GO
;
NOTSYS:	MOV BX,SECTBL
	XCHG BX,DX
	DEC CX
	CALL BSECTTRAN
	MOV AL,BYTE PTR SPT+1	;IF SPT<256 (HI-ORD = 0)
	OR AL,AL	; THEN FORCE 8-BIT TRANSLATION
	JNZ VSCTR1	; ELSE KEEP ALL 16 BITS
	MOV BH,AL
VSCTR1:
GSTSEC:
	MOV PHYSEC,BX
	cmp byte ptr cpm_version,31h
	jb aint31
	  mov bx,cursec	;version 31 does it's own xlate, use cursec
aint31:
	MOV CX,BX
	CALL SEC	;SET SECTOR THRU BIOS
	POP BX
	RET
;
OUTLIM:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++not within tracks 0-',0
	PUSH BX
	MOV BX,MAXTRK
	CALL DEC
	POP BX
	CALL ILPRT
	DB	'++'
	DB	CR,LF,0
	CALL NORITE
	JMP PRMPTR
;
SETDMA:
	CALL DMA	;SET UP DMA FOR BIOS
	MOV CX,CS	;SET DMA SEGMENT FOR BIOS
	JMP SETDMAB
;
;
READ:	MOV AL,1
	MOV  WRFLG,AL
	PUSH BX
	CALL DSKREAD	;READ DISK THRU BIOS
	OR AL,AL
	JZ READOK
	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++READ failed, sector may be invalid++'
	DB	CR,LF,0
;
READOK:	POP BX
	RET
;
WRITE:	MOV AL,WRFLG
	OR AL,AL
	JNZ PWRITE
;
BADW:	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++CANNOT WRITE UNLESS READ ISSUED'
	DB	CR,LF,0
	JMP EXPL
;
PWRITE:	PUSH BX
	MOV CL,1	;FORCE WRITE TYPE 1 IN CASE 2.x DEBLOCK USED
	CALL DSKWRITE	;WRITE DISK
	OR AL,AL
	JZ WRITOK
	XOR AL,AL
	MOV  QFLAG,AL
	CALL ILPRT
	DB	'++WRITE failed++',CR,LF,0
;
WRITOK:	POP BX
	RET
;
;Help
;
HELP:	CALL ILPRT
	DB	'Operands in brackets [...] are optional'
	DB	CR,LF
	DB	'Numeric values: ''n'' are decimal, ''x'' hex'
	DB	CR,LF,CR,LF
	DB	'+[n]   step in [n] sectors;'
	DB	CR,LF
	DB	'-[n]   step out [n] sectors'
	DB	CR,LF
	DB	'#      print disk parameters for curr drive.'
	DB	CR,LF
	DB	'=xxx   search for ASCII xxx from curr sector.'
	DB	CR,LF
	DB	'       Caution: upper/lower case matters.'
	DB	CR,LF
	DB	'       Use <xx> for hex:'
	DB	CR,LF
	DB	'       To find "IN AL,0C0h" use: =<E4><C0>     or'
	DB	CR,LF
	DB	'       "(tab)H,0(CR)(LF)" use: =<9>H,0<D><A>'
	DB	CR,LF
	DB	'<      save current sector into mem. buff.'
	DB	CR,LF
	DB	'>      restore saved sector'
	DB	CR,LF
	DB	'?      give help'
	DB	CR,LF
	DB	'A[ff,tt] ASCII dump'
	DB	CR,LF,CR,LF
	DB	'(Type SP bar to continue)'
	DB	1,CR,LF,CR,LF
	DB	'C      Change:'
	DB	CR,LF
	DB	'       CHaddr,byte,byte... (hex)'
	DB	CR,LF
	DB	'  or   CAaddr,data...  (Ascii)'
	DB	CR,LF
	DB	'       <xx> Allowed for imbedded hex.'
	DB	CR,LF
	DB	'  or   CHfrom-thru,byte  e.g. ch0-7f,e5'
	DB	CR,LF
	DB	'  or   CAfrom-thru,byte'
	DB	CR,LF
	DB	'D[ff,tt] Dump (hex+ASCII)'
	DB	CR,LF
	DB	'Fn.t   Find file'
	DB	CR,LF
	DB	'Gnn    CP/M Allocation Group nn'
	DB	CR,LF
	DB	'H[ff,tt]       hex dump'
	DB	CR,LF
	DB	'L      Log in drive'
	DB	CR,LF
	DB	'Lx     Log in drive x'
	DB	CR,LF
	DB	'M[nn]  Map [from group nn]'
	DB	CR,LF,CR,LF
	DB	'(Type SP bar to continue)'
	DB	1,CR,LF,CR,LF
	DB	'N      New disk'
	DB	CR,LF
	DB	'P      Toggle printer switch'
	DB	CR,LF
	DB	'Q      Quiet mode (no msgs)'
	DB	CR,LF
	DB	'R      Read current sector'
	DB	CR,LF
	DB	'Snn    Sector nn'
	DB	CR,LF
	DB	'Tnn    Track nn'
	DB	CR,LF
	DB	'Unn    Set User nn for Find command'
	DB	CR,LF
	DB	'V[nn]  View [nn] ASCII sectors'
	DB	CR,LF
	DB	'W      Write current sector'
	DB	CR,LF
	DB	'X      Exit program'
	DB	CR,LF
	DB	'Z[nn]  Sleep [nn tenths]'
	DB	CR,LF
	DB	'/[nn]  Repeat [nn (decimal) times]'
	DB	CR,LF,CR,LF
	DB	'(Type SP bar to continue)'
	DB	1,CR,LF,CR,LF
	DB	'Cancel a function with ESC or Ctl-C.'
	DB	CR,LF
	DB	'Suspend output with S or Ctl-S.'
	DB	CR,LF
	DB	'Separate commands with "!".'
	DB	CR,LF
	DB	'       Example: g0'
	DB	CR,LF
	DB	'       +!d!z#20!/'
	DB	CR,LF
	DB	'       would step in, dump, sleep 2 sec, '
	DB	CR,LF
	DB	'       and repeat until control-c typed.'
	DB	CR,LF
	DB	'All "nn" usage except "/", "T", and "S" are'
	DB	CR,LF
	DB	'        HEX.  Use #nn for decimal.'
	DB	CR,LF,CR,LF
	DB	'See DU.DOC for complete examples.'
	DB	CR,LF,CR,LF,0
	JMP PROMPT
;
;********************************
;*				*
;*    Utility Subroutines	*
;*				*
;********************************
;
GRPCMP:	MOV AL,CL
	INC DH
	DEC DH
	JZ CMP8
	CMP AL,BYTE PTR [BX]
	PUSHF
	INC BX
	POPF
	JZ L@00231
	RET
L@00231:
	MOV AL,CH
;
CMP8:	CMP AL,BYTE PTR [BX]
	RET
;
;2's complement HL ==> HL
;
NEG:
	NOT BX
	PUSHF
	INC BX
	POPF
	RET
;
;HL/2 ==> HL
;
ROTRHL:	OR AL,AL
	MOV AL,BH
	RCR AL,1
	MOV BH,AL
	MOV AL,BL
	RCR AL,1
	MOV BL,AL
	RET
;
;Collect the number of '1' bits
;in A as a count in C
;
COLECT:	MOV CH,8
;
COLOP:	RCL AL,1
	JAE COSKIP
	INC CL
;
COSKIP:	DEC CH
	JNZ COLOP
	RET
;
;HL-DE ==> HL
;
SUBDE:
	SUB BX,DX
	RET
;
;Quick Kludge multiply
;HL=DE ==> HL
;
MULT:	PUSH CX
	PUSH DX
	XCHG BX,DX
	MOV CX,DX
	MOV AL,CH
	OR AL,CL
	JNZ MULCON
	MOV BX,OFFSET 0	;FILTER SPECIAL CASE
	JMP MLDONE	;  OF MULTIPLY BY 0
;
MULCON:
	DEC CX
	MOV DX,BX
;
MULTLP:	MOV AL,CH
	OR AL,CL
	JZ MLDONE
	ADD BX,DX
	DEC CX
	JMP MULTLP
;
MLDONE:	POP DX
	POP CX
	RET
;
;Routine to fill in disk params
;with every drive change
;
LOGIT:
	cmp byte ptr cpm_version,31h 	;if it's ver 31 the select
	jae logcal			;routine done moved DPB
	  MOV DX,OFFSET DPB	;   THEN MOVE TO LOCAL
	  MOV CH,DPBLEN	;  WORKSPACE
	  CALL MOVEFROMBIOS
;
LOGCAL:	MOV BX,OFFSET GRPDIS
	MOV AL,BYTE PTR [BX]
	PUSH AX
	MOV AL,BLM
	MOV BYTE PTR [BX],AL
	PUSH BX
	MOV BX,DSM
	XCHG BX,DX
	CALL GTKSEC
	MOV MAXSEC,BX
	XCHG BX,DX
	MOV MAXTRK,BX
	POP BX
	POP AX
	MOV BYTE PTR [BX],AL
	RET
;
;
DATAOFFSET EQU OFFSET$
	DSEG
	ORG DATAOFFSET
;
;
;
	RW 200
STACK	RW 1	;LOCAL STACK
;
;Temporary storage area
;
BUFAD	DW	BASE +100H ;FORCES INITIAL READ
HEXAD	DW	0	;TO RE-FETCH A VALUE
TOGO	DW	0FFFFH	;REPEAT COUNT (FFFF=CONT)
TWOUP	DB	0
PFLAG	DB	0	;1=PRINT
GROUP	DW	0
GRPDIS	DB	0
SAVEFL	DB	0
CURTRK	DW	0
CURSEC	DW	1
PHYSEC	DW	1
TABCOL	DB	0
FILECT	DW	0
DIRPOS	DB	0
FINDFL	DB	0	;1=MUST POSITION AFTER FIND
FTSW	DB	1	;SEARCH W/O INCREMENT
NOTPOS	DB	1	;INITIALLY NOT POSITIONED
WRFLG	DB	0	;MAY NOT WRITE UNTIL '+', '-',
;			 OR 'G' COMMAND
QFLAG	DB	0	;QUIET? (0=NO)
FIRST0	DB	0	;SETS TO 0 IF FIRST SEC # IS 0
UNUM	DB	0	;USER NUMBER
DRIVE	DB	0
MAXTRK	DW	0
MAXSEC	DW	0
SECTBL	DW	0	;POINTER TO SECTOR SKEW TABLE
;
BACK	RW 1	;TO BACK UP IN "CA0-7F,X"
DUMTYP	RS 1
;
;--------------------------------------------------

bios_call_tbl 	rw	1		;address of bios call table goes here

table11	dw	offset select_disk11	;direct bios calls for cp/m-11
	dw	offset set_track11
	dw	offset set_dmaseg11
	dw	offset set_dmaoff11	
	dw	offset set_sector11
	dw	offset read_sector11	
	dw	offset write_sector11 	 
	dw	offset sector_xlat11	
	dw	offset home_disk11

table31	dw	offset select_disk31	;direct bios calls for cp/m-31
	dw	offset set_track31
	dw	offset set_dmaseg31
	dw	offset set_dmaoff31	
	dw	offset set_sector31
	dw	offset read_sector31	
	dw	offset write_sector31 	 
	dw	offset sector_xlat31	
	dw	offset home_disk31

bpb		rs	0		;bios parameter block
bpb_func	rb	1		;for direct bios call 50
bpb_cx		rw	1
bpb_dx		rw	1

cpm_version	dw	0		;cpm version

sysaddr		dw	0		;SYSDAT addr
udaaddr		dw	0		;process UDA addr

imcnt		db	1		;multi-sector count (preset to 1)
idrive		rb	1		;drive number (a:=0)
itrack		rw	1		;track number (first track=0)
isector		rw	1		;sector number (first sector=0)
idmaseg		rw	1		;dma segment for sector buffer
idmaoff		dw	offset sector_buf ;dma offset for sector buffer

dmaseg		rw	1		;dma segment for data
dmaoff		rw	1		;dma offset for data

trk_sect	dw	0ffffh,0	;number of track/sector in memory

sec_xlat_tbl	dw	0		;address of BIOS xlat table

rec_offset	dw	0		;logical record offset into phy sec

;  data storage for Disk Parameter Block (DPB)
;
dpb		rs	0		;17 bytes of storage
;
SPT		rw	1
BSH		rb	1
BLM		rb	1
EXM		rb	1
DSM		rw	1
DRM		rw	1
AL0		rb	1
AL1		rb	1
CKS		rw	1
SYSTRK		rw	1
PHYSHF		rb	1		;not in CP/M-86 ver 1.1
PHYMSK		rb	1		; "   "   "      "   "
;
;End of disk parameter block

wvmsg		db	13,10,'Only CP/M-86 ver 1.1 & 3.1 Plus '
		db	'and CCP/M ver 3.1-4.1 are Supported$'
mpmsg		db	13,10,'THE MP/M OPERATING SYSTEM IS NOT SUPPORTED$'

toobig		db	13,10,'SECTOR SIZE TOO BIG - ABORTING$'

sector_buf	rs	2048

;--------------------------------------------------
;
SAVBUF	RS 128
INBUF	RS 128
;
;Directory read in here; also search work area
;
WORK	EQU	$
DIRECT	EQU	$
	END
